#pragma once
#include <Preprocessor.h>
#include <assimp/Importer.hpp>
#include <assimp/postprocess.h>
#include <assimp/scene.h>
#include <glm/ext.hpp>
#include <glm/glm.hpp>
#include <string>
#include <vector>

namespace Umikaze::Core {

class Geometry {
  CORE_OBJECT
public:
  virtual ~Geometry() = 0;

private:
  virtual void draw() = 0;
};

class InternalGeometry : public Geometry {
public:
  virtual ~InternalGeometry() override;

protected:
  uint32_t _vao = 0, _vbo = 0;
};

class CubeGeometry : public InternalGeometry {
public:
  CubeGeometry(float width, float height, float depth) {
    _width = width;
    _height = height;
    _depth = depth;
  }

private:
  virtual void draw() override;

private:
  float _width, _height, _depth;
};

class SphereGeometry : public InternalGeometry {
public:
  SphereGeometry(float radius, uint32_t numCircles, uint32_t numLines) {
    _radius = radius;
    _numCircles = numCircles;
    _numLines = numLines;
  }

private:
  virtual void draw() override;

private:
  float _radius;
  uint32_t _numCircles = 16, _numLines = 16;
  uint32_t _numVertices;

  void generateSphere(std::vector<glm::vec3> &vertices,
                      std::vector<glm::vec3> &normals,
                      std::vector<glm::vec2> &uvs) {
    auto circle = generateHalfCircle();
    float pi = glm::pi<float>();
    float halfPi = pi / 2.0f;
    float doublePi = 2 * pi;
    float xstep = doublePi / _numCircles;
    float ystep = pi / _numLines;
    float xv = 0.0f;
    float yv = 0.0f;

    for (uint32_t i = 0; i < (_numLines - 1); ++i, yv += ystep) {

      float currentRad = yv - halfPi;
      float currentNxz = glm::cos(currentRad);
      float currentNy = glm::sin(currentRad);

      float nextRad = currentRad + ystep;
      float nextNxz = glm::cos(nextRad);
      float nextNy = glm::sin(nextRad);
      for (uint32_t j = 0; j <= _numCircles; ++j, xv += xstep) {
        float cos = glm::cos(xv);
        float sin = glm::sin(xv);

        float currentNx = currentNxz * cos;
        float currentNz = currentNxz * sin;
        vertices.emplace_back(currentNx * _radius, currentNy * _radius,
                              currentNz * _radius);
        normals.emplace_back(currentNx, currentNy, currentNz);
        uvs.emplace_back(xv / doublePi, yv / pi);

        float nextNx = nextNxz * cos;
        float nextNz = nextNxz * sin;
        vertices.emplace_back(nextNx * _radius, nextNy * _radius,
                              nextNz * _radius);
        normals.emplace_back(nextNx, nextNy, nextNz);
        uvs.emplace_back(xv / doublePi, (yv + ystep) / pi);
      }
    }
  }

  std::vector<glm::vec2> generateHalfCircle() {
    float pi = 3.14159267f;
    float step = pi / _numLines;
    float halfPi = pi / 2.0f;
    std::vector<glm::vec2> points;
    float v = 0.0f;
    for (uint32_t i = 0; i < _numLines; ++i, v += step) {
      float rad = v - halfPi;
      float x = glm::cos(rad);
      float y = glm::sin(rad);
      points.push_back({x, y});
    }

    return points;
  }
};

class PlaneGeometry : public InternalGeometry {
public:
  PlaneGeometry(float width, float height) {
    _width = width;
    _height = height;
  }

private:
  virtual void draw() override;

private:
  float _width, _height;
};

// class BufferGeometry {

//   void setAttribute(const std::string &name,
//                     const std::shared_ptr<Attribute> &attribute) {}

//   // std::shared_ptr<Attribute> getAttribute(const std::string &name) {}

//   // std::map<std::string, std::shared_ptr<Attribute>> _attributes;
// };

} // namespace Umikaze::Core